# -*- coding:utf-8 -*-
import time, requests, pymysql, base64, json, random, hashlib, re
from Crypto.Cipher import AES

# 要想使用请确保一下库已安装且正常：
# time  --  Python 自带库 用于延迟获取时间等
# requests  --  用于发送请求 需要手工安装
# pymysql  --  用于连接数据库 需要手工安装  备注：参考文档说明 https://www.python.org/dev/peps/pep-0249
# base64  --  用于加密解密
# json  --  加密解密json数据
# random  --  Python 自带库 用于获取随机数
# Crypto -- 用于反反爬 AES加解密等
# |（tips：这个库比较难装，安装方法 pip install Crypto ，然后去Python根目录下把Lib\site-packages中，把所有crypto字样文件夹都改成Crypto）
# hashlib -- 哈希值计算
# re  --  正则表达式

# ------ 这里填写SQL数据库信息 ------

# sqlServer —— 数据库网路地址
# sqlPort —— 数据库端口
# sqlUsername —— 数据库用户名
# sqlPassword —— 数据库密码
# sqlName —— 对应数据库名

sqlServer = "127.0.0.1"
sqlPort = 3306
sqlUsername = "root"
sqlPassword = "root"
sqlName = "webpy"

# ------ 这里填写SQL数据库信息 ------


# ------ 这里填写邮件服务器信息 ------

# P.S. >>>
# 会自动获取数据库的数据并进行发邮件。不想发邮件的把 emailDing 改为 False
# Python有专用于发邮件的模块 但是我实在是懒得写了（麻烦）
# 若不想使用API接口发信请自行修改 send_email() 函数。在170行左右，请不要随意修改send_email()的参数（除非你想把整个代码都修改的话）
# PHP API 的 Email接口默认在 文件夹 core/ 下 email.php ，可以自行加上验证。

# emailTest —— 启动时进行邮箱发信测试
# emailUrl —— 发邮件API地址
# emailDing —— 启用邮件提醒
# emailKey —— 接口调用密匙
# emailTestEmail —— 测试发送邮件时接收的用户

emailTest = False
emailUrl = 'http://127.0.0.1/core/email.php'
emailDing = True
emailKey = 'Pikachu'
emailTestEmail = '422880152@qq.com'

# ------ 这里填写邮件服务器信息 ------


# 保存文件后请运行Python
# 当提示“自检已完成”的时候您就可以使用了。
# 请非专业人员不要继续编辑，防止代码错误损坏。

# 版权说明：
# 由我叫以赏创作 目前免费提供大版本的更新 小更新将在付费后发放
# 个人网站 http://blog.zhangyishang.top
# 购买完整版：（待定目前尚未发布完整版，完整版将会更加完善）


user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 " \
             "Safari/537.36 Edg/84.0.522.52"  # 浏览器标识
randomString = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"  # 随机字符串



def printW(*args, who='system', sep=" ", end="\n"):
    print(time.strftime("[%Y-%m-%d %H:%M:%S] ") + who + "> " + sep.join('%s' % x for x in args), end=end)


checktime_start = time.time()
printW("开始检查程序运行......")
printW("正在连接数据库......")


def reconnect_db():
    """重连数据库"""
    global cursor, db
    db.close()
    printW("正在重新连接数据库......")
    db = pymysql.connect(user=sqlUsername, password=sqlPassword, host=sqlServer, port=sqlPort,
                         database=sqlName)  # 连接数据库
    cursor = db.cursor(pymysql.cursors.DictCursor)  # 使用 cursor() 方法创建一个游标对象 cursor


def test_db():
    """测试数据库"""
    printW("webpy_log:", "数据总数", sql_query("select * FROM webpy_log")[0], "个  ......OK!")
    printW("webpy_part:", "数据总数", sql_query("select * FROM webpy_part")[0], "个  ......OK!")
    printW("webpy_system:", "数据总数", sql_query("select * FROM webpy_system")[0], "个  ......OK!")
    printW("webpy_user:", "数据总数", sql_query("select * FROM webpy_user")[0], "个  ......OK!")
    sql_query("UPDATE `webpy_system` SET `setting_value`='%s' WHERE  `setting_key`='test_config'" % (
        random.randint(10000, 99999)))
    printW("Change Data:", "webpy_system -> test_config  ......OK!")

def sql_query(query: str):
    """读取数据库"""
    global cursor, db
    cursor.execute(query)
    data = cursor.fetchall()
    db.commit()
    return (len(data), data)


def encode_string(string: str):
    """加密函数 若修改了加解密函数且数据库不是第一次导入请修改对应数据库内容
    修改后请修改对应的PHP代码"""
    if string == None:
        string = ""
    return base64.b64encode(string.encode()).decode()


def decode_string(string: str):
    """解密函数 若修改了加解密函数且数据库不是第一次导入请修改对应数据库内容
        修改后请修改对应的PHP代码"""
    if string == None:
        string = ""
    return base64.b64decode(string.encode()).decode()


def get_userdata(partname):
    """获取指定任务类型的用户数据 多个数据列表"""
    global emailTestEmail
    result = []
    all = sql_query("select * FROM webpy_part where partname = '%s'" % (partname))[1]
    for x in all:
        temp = json.loads("{}" if decode_string(x['userdata']) == "" else decode_string(x['userdata']))
        if 'state' not in temp or temp['state'] != 'on':
            continue
        temp['partcard'] = x['partcard']
        temp['username'] = x['username']
        temp['partname'] = x['partname']
        temp['partcalled'] = x['partcalled']
        result.append(temp)
    return result


def get_username():
    """获取所有已激活的用户"""
    return sql_query("select * FROM webpy_user where level <> '0'")[1]


def get_userdata_from_username(username):
    """通过用户名来获取用户数据"""
    result = []
    all = sql_query("select * FROM webpy_part where username = '%s'" % (username))[1]
    for x in all:
        temp = json.loads("{}" if decode_string(x['userdata']) == "" else decode_string(x['userdata']))
        if 'state' not in temp or temp['state'] != 'on':
            continue
        temp['partcard'] = x['partcard']
        temp['username'] = x['username']
        temp['partname'] = x['partname']
        temp['partcalled'] = x['partcalled']
        temp['returndata'] = json.loads(
            "{}" if decode_string(x['returndata']) == "" else decode_string(x['returndata']))
        result.append(temp)
    return result


def add_userlog(username, log):
    """增加用户日志"""
    logA = sql_query("select * FROM webpy_log where username='%s'" % (username))[1][0]['log']
    if logA == None:
        logA = ""
    logA += time.strftime("[%Y-%m-%d %H:%M:%S] ") + log.replace('\n', '//') + '\r\n'
    sql_query("update webpy_log set log='%s' where username='%s'" % (logA.replace("'", "\\'"), username))


def add_retrndata(partcard, content, date=None):
    """增加任务数据"""
    date = time.strftime("%Y/%m/%d") if date == None else date
    temp = sql_query("select * FROM webpy_part where partcard='%s'" % (partcard))[1][0]['returndata']
    returndataA = json.loads('{}' if temp in ('', None) else decode_string(temp))  # type: dict
    returndataA[date] = content
    sql_query("update webpy_part set returndata='%s' where partcard='%s'" % (
        encode_string(json.dumps(returndataA)), partcard))


def send_email(To, Name, Subject, Text):
    """发送邮件 用于提醒用户签到结果"""
    data = {
        'emailKey': emailKey,
        'To': To,
        'Name': Name,
        'Subject': Subject,
        'Text': Text
    }
    try:
        result = requests.post(emailUrl, data=data).json()
        printW("邮件API返回详情：", result)
        if 'message' in result and result['message'] == 'success':
            return True
        else:
            return False
    except BaseException as error:
        printW("发送邮件过程中返回的数据：", str(error))
        return False


def email_report():
    """生成用户的邮件报告"""
    if emailDing == False:
        return
    date = time.strftime("%Y/%m/%d")
    for userinfo in get_username():
        username = userinfo['username']
        email = emailTestEmail if userinfo['email'] in (None, '') else userinfo['email']
        message = ""
        times = 0
        for userdata in get_userdata_from_username(username):
            returndata = userdata['returndata']
            times += 1
            if date not in returndata:
                returndata[date] = ["程序未执行", "程序未执行"]
            message += f"""
            <h4>{times}.任务编号：{userdata['partcard']}&nbsp; &nbsp;任务类别：{userdata['partname']}&nbsp; &nbsp;任务名称：{userdata['partcalled']}</h4>
            <p>&nbsp; &nbsp; &nbsp; &nbsp;任务状态：{returndata[date][0]}</p>
            <p>&nbsp; &nbsp; &nbsp; &nbsp;任务详情：{returndata[date][1]}</p>
            <br><br><br>
            """

        text = f"""<h1 style="text-align:center;"><span style="font-weight:bold;">这是来自</span><span style="font-weight:bold;font-family:Consolas, monospace;"> Webpy </span><span style="font-weight:bold;">自动签到的一封报告单</span></h1>
                <h3 style="text-align:center;"><span style="font-weight:bold;">报告时间：{date}&nbsp; &nbsp; &nbsp; 来自用户：{username}&nbsp; &nbsp; &nbsp; 此为一天任务的报告。</span></h3>"""
        text += message
        send_email(email, username, date + " 的自动签到任务执行报告单", text)


# ---------------  从这里开始写自动签到函数 ---------------


def template():
    """默认模板 方便套用"""
    partname = ""
    printW("签到任务执行中......", who=partname)
    printW("获取任务数量中......", who=partname)
    userdata = get_userdata(partname)
    printW("签到任务总数：", len(userdata), "个", who=partname)
    times = 1  # 记录次数
    for info in userdata:
        if 'partcard' not in info:
            continue
        printW(f"任务编号:{times},任务ID:{info['partcard']},正在执行......", who=partname)
        try:
            add_userlog(info['username'], partname + '> 签到返回结果：' + str(""))
            add_retrndata(info['partcard'], ["", ""])
        except BaseException as error:
            add_userlog(info['username'], partname + '> 签到任务失败！原因如下：' + str(error))
            add_retrndata(info['partcard'], ['签到失败', '签到任务失败！原因如下：' + str(error)])
        printW(f"任务编号:{times},任务ID:{info['partcard']},执行完毕！", who=partname)
    printW("签到任务完成！", who=partname)


def Acfun_Dosign():
    """Acfun签到任务 by Yishang"""
    partname = "Acfun"
    printW("签到任务执行中......", who=partname)
    printW("获取任务数量中......", who=partname)
    userdata = get_userdata(partname)
    printW("签到任务总数：", len(userdata), "个", who=partname)
    times = 1  # 记录次数
    for info in userdata:
        if 'partcard' not in info:
            continue
        printW(f"任务编号:{times},任务ID:{info['partcard']},正在执行......", who=partname)
        try:
            if info['cookie'].strip() in ('', None):
                continue
            # 构造验证密匙
            certified = base64.b64encode(''.join(random.sample(randomString, 11)).encode()).decode()
            certified_cookie = info['cookie']  # type:str

            # 构造验证密匙的cookie
            a = certified_cookie.find('stochastic=')
            b = certified_cookie.find(';', a)
            b = len(certified_cookie) if b == -1 else b
            certified_cookie = certified_cookie[:a] + certified_cookie[b:]
            certified_cookie += f"stochastic={certified.replace('=', '%3D')}"

            # 发送签到请求
            # 领取每日香蕉
            result = requests.post(
                f"https://www.acfun.cn/nd/pst?locationPath=signin&certified={certified}&channel=0&data={int(time.time() * 1000)}",
                headers={"cookie": certified_cookie, "User-Agent": user_agent}).json()

            time.sleep(1)  # 防和谐延迟

            # 获取用户信息
            Userinfo = requests.get("https://www.acfun.cn/rest/pc-direct/user/personalInfo",
                                    headers={"cookie": info['cookie'], "User-Agent": user_agent}).json()

            time.sleep(1)  # 防和谐延迟

            # 二次查看是否签到成功
            html = requests.get("https://www.acfun.cn/member/",
                                headers={"cookie": info['cookie'], "User-Agent": user_agent}).content.decode()

            if 'msg' not in result and 'message' not in result:
                raise BaseException('服务器放回了错误的数据，这是我们捕获到的一些信息：' + str(result))

            banana = Userinfo['info']['banana']
            add_userlog(info['username'], partname + '> 签到返回结果：' + str(result))
            add_retrndata(info['partcard'], ['香蕉数 ' + str(banana) + " 个" + ("，已签到" if '已签到' in html else "，签到失败"),
                                             '签到返回数据：' + (result['msg'] if 'msg' in result else result[
                                                 'message']) + "<br>" + "用户目前香蕉数：" + str(banana) + " 个"])
        except BaseException as error:
            add_userlog(info['username'], 'Acfun> 签到任务失败！原因如下：' + str(error))
            add_retrndata(info['partcard'], ['签到失败', '签到任务失败！原因如下：' + str(error)])
        printW(f"任务编号:{times},任务ID:{info['partcard']},执行完毕！", who=partname)
    printW("签到任务完成！", who=partname)


def BiliBili_Dosign():
    """BiliBili签到"""
    partname = "BiliBili"
    printW("签到任务执行中......", who=partname)
    printW("获取任务数量中......", who=partname)
    userdata = get_userdata(partname)
    printW("签到任务总数：", len(userdata), "个", who=partname)
    times = 1  # 记录次数
    for info in userdata:
        if 'partcard' not in info:
            continue
        printW(f"任务编号:{times},任务ID:{info['partcard']},正在执行......", who=partname)
        try:
            cookie = info['cookie']
            if cookie.strip() in ('', None):
                continue
            result1 = requests.get('https://api.live.bilibili.com/xlive/web-ucenter/v1/sign/DoSign',
                                   headers={'user-agent': user_agent, 'Cookie': cookie}).json()
            result2 = requests.get('https://account.bilibili.com/site/getCoin',
                                   headers={'user-agent': user_agent, 'Cookie': cookie}).json()
            result3 = requests.get('https://api.live.bilibili.com/live_user/v1/UserInfo/live_info',
                                   headers={'user-agent': user_agent, 'Cookie': cookie}).json()
            # 返回状态码部分说明
            # -101 -- 账号未登录
            # 1011038 -- 操作太快
            # 1011040 -- 已签到过
            # 0 -- 签到成功

            # 操作太快行为
            error_times = 0
            while result1['code'] == 1011038 and error_times < 3:
                time.sleep(3)
                error_times += 1
                result1 = requests.get('https://api.live.bilibili.com/xlive/web-ucenter/v1/sign/DoSign',
                                       headers={'user-agent': user_agent, 'Cookie': cookie}).json()
            if error_times >= 3:
                raise BaseException('服务器无法接收我们的请求，请联系管理员。')

            # 判断其它返回值
            if 'code' not in result1 or 'message' not in result1:
                raise BaseException('服务器返回了错误的数据！返回的数据' + str(result1))
            if result1['code'] == -101:
                raise BaseException('登录失效！<br>请重新填写cookie数据！返回的返回签到结果：' + result1['message'])

            # 处理硬币银瓜子
            coin = result2['data']['money']
            silvers = result3['data']['userCoinIfo']['silver']

            if result1['code'] == 1011040 or result1['code'] == 0:
                add_userlog(info['username'], partname + '> 签到返回结果：' + str(result1))
                add_retrndata(info['partcard'], [f"硬币数 {coin} 个， 银瓜子 {silvers} 个", "服务器返回签到结果：" + result1['message']])
                continue

        except BaseException as error:
            add_userlog(info['username'], partname + '> 签到任务失败！原因如下：' + str(error))
            add_retrndata(info['partcard'], ['签到失败', '签到任务失败！<br>原因如下：' + str(error)])
        printW(f"任务编号:{times},任务ID:{info['partcard']},执行完毕！", who=partname)
    printW("签到任务完成！", who=partname)


def iqiyi_Dosign():
    """爱奇艺签到 Tip：爱奇艺反爬反吐了 干脆不用你的反爬"""
    partname = "iqiyi"
    printW("签到任务执行中......", who=partname)
    printW("获取任务数量中......", who=partname)
    userdata = get_userdata(partname)
    printW("签到任务总数：", len(userdata), "个", who=partname)
    times = 1  # 记录次数
    for info in userdata:
        if 'partcard' not in info:
            continue
        printW(f"任务编号:{times},任务ID:{info['partcard']},正在执行......", who=partname)
        try:
            # data中返回状态的部分解释
            # A0000 -- 签到成功
            # A0002 -- 已签到过
            # A00101 -- 接口数据效验失败
            result = requests.get(info['url'], headers={"User-Agent": user_agent}).json()
            if 'data' not in result:
                raise BaseException("服务器返回数据错误！返回内容：" + str(result))
            result = result['data'][0]
            if result['code'] == 'A00101':
                raise BaseException("用户自动签到URL已失效。")
            elif result['code'] == 'A0002' or result['code'] == 'A0000':
                add_userlog(info['username'], partname + '> 签到返回结果：' + str(result))
                add_retrndata(info['partcard'], ["签到成功", "成功执行"])
            else:
                add_userlog(info['username'], partname + '> 签到返回结果：' + str(result))
                add_retrndata(info['partcard'], ["签到状态未知", "服务器返回数据：" + str(result['message'])])
        except BaseException as error:
            add_userlog(info['username'], partname + '> 签到任务失败！原因如下：' + str(error))
            add_retrndata(info['partcard'], ['签到失败', '签到任务失败！原因如下：' + str(error)])
        printW(f"任务编号:{times},任务ID:{info['partcard']},执行完毕！", who=partname)
    printW("签到任务完成！", who=partname)


def wyy_music_Dosign():
    """网易云自动签到刷单 Python反爬来自：https://github.com/SakurajimMai/wyymusic"""
    partname = "wyy"
    printW("签到任务执行中......", who=partname)
    printW("获取任务数量中......", who=partname)
    userdata = get_userdata(partname)
    printW("签到任务总数：", len(userdata), "个", who=partname)
    times = 1  # 记录次数
    for info in userdata:
        if 'partcard' not in info:
            continue
        printW(f"任务编号:{times},任务ID:{info['partcard']},正在执行......", who=partname)
        try:
            message = ""  # 最后返回的信息
            messageX = ""  # 最后返回的详细信息

            # 获取用户的手机号与md5的密码
            phone = info['phone']
            passmd5 = info['passmd5']

            # 构造登录表单 与 基本的头部信息
            logindata = {"phone": phone, "countrycode": "86", "password": passmd5.lower(), "rememberLogin": "true"}
            headers1 = {'User-Agent': user_agent, "Referer": "http://music.163.com/",
                        "Accept-Encoding": "gzip, deflate"}
            headers2 = {'User-Agent': user_agent, "Referer": "http://music.163.com/",
                        "Accept-Encoding": "gzip, deflate",
                        "Cookie": "os=pc; osver=Microsoft-Windows-10-Professional-build-10586-64bit; appver=2.0.3.131777; channel=netease; __remember_me=true;"}

            # 创建一个热连接
            wyy = requests.Session()

            # 定义各种要用到的URL
            url1 = "https://music.163.com/weapi/login/cellphone"
            url2 = "https://music.163.com/weapi/point/dailyTask"
            url3 = "https://music.163.com/weapi/v1/discovery/recommend/resource"

            # 定义函数
            def encrypt(key, text):
                """网易的可爱加密机制"""
                cryptor = AES.new(key.encode('utf8'), AES.MODE_CBC, b'0102030405060708')
                length = 16
                count = len(text.encode('utf-8'))
                if (count % length != 0):
                    add = length - (count % length)
                else:
                    add = 16
                pad = chr(add)
                text1 = text + (pad * add)
                ciphertext = cryptor.encrypt(text1.encode('utf8'))
                cryptedStr = str(base64.b64encode(ciphertext), encoding='utf-8')
                return cryptedStr

            # 网易生成验证params与encSecKey的函数
            protect = lambda text: {"params": encrypt('TA3YiYCfY2dDJQgg', encrypt('0CoJUm6Qyw8W8jud', text)),
                                    "encSecKey": "84ca47bca10bad09a6b04c5c927ef077d9b9f1e37098aa3eac6ea70eb59df0aa28b691b7e75e4f1f9831754919ea784c8f74fbfadf2898b0be17849fd656060162857830e241aba44991601f137624094c114ea8d17bce815b0cd4e5b8e2fbaba978c6d1d14dc3d1faf852bdd28818031ccdaaa13a6018e1024e2aae98844210"}

            # md5生成
            md5 = lambda string: hashlib.md5().update(string.encode(encoding='utf-8')).hexdigest()

            # 登录网易云音乐
            result = wyy.post(url=url1, data=protect(json.dumps(logindata)), headers=headers2)

            tempcookie = result.cookies

            result = result.json()
            if 'code' not in result:
                raise BaseException("执行网易云任务时，服务器返回了错误的数据。")
            # 检查登录
            if result['code'] != 200:
                raise BaseException("登录网易云音乐时，服务器验证账号密码错误！请检查账号密码！错误代码：" + str(result['code']))
            add_userlog(info['username'], partname + '> 登录返回结果：登录成功！')

            # 签到任务
            result = wyy.post(url=url2, data=protect('{"type":0}'), headers=headers1).json()
            if 'code' not in result:
                raise BaseException("执行网易云任务时，服务器返回了错误的数据。")
            if result['code'] != 200 and result['code'] != -2:
                raise BaseException("网易云音乐签到时，发生了错误，错误数据：" + str(result))
            else:
                if result['code'] == 200:
                    message += "经验 + " + str(result['point']) + "，"
                    messageX += "网易云签到任务完成！经验增加：" + str(result['point']) + "<br>返回数据详情：<br>" + str(result) + "<br>"
                    add_userlog(info['username'],
                                partname + '> 返回结果：' + "网易云签到任务完成！经验增加：" + str(result['point']) + "返回数据详情：" + str(
                                    result))
                else:
                    message += "重复签到，"
                    messageX += "网易云签到任务重复签到！" + "<br>返回数据详情：" + str(result) + "<br>"
                    add_userlog(info['username'], partname + '> 返回结果：' + "网易云签到任务重复签到！" + "返回数据详情：" + str(result))

            # 自动刷单
            buffer = []
            count = 0
            result = wyy.post(url=url3, data=protect(
                '{"csrf_token":"' + requests.utils.dict_from_cookiejar(tempcookie)['__csrf'] + '"}'),
                              headers=headers1).json()
            if 'recommend' not in result:
                raise BaseException("执行网易云任务时，服务器返回了错误的数据。")
            for x in result['recommend']:
                url = 'https://music.163.com/weapi/v3/playlist/detail?csrf_token=' + \
                      requests.utils.dict_from_cookiejar(tempcookie)['__csrf']
                data = {
                    'id': x['id'],
                    'n': 1000,
                    'csrf_token': requests.utils.dict_from_cookiejar(tempcookie)['__csrf'],
                }
                result = wyy.post(url, protect(json.dumps(data)), headers=headers1).json()
                buffer = []
                count = 0
                if 'playlist' not in result:
                    raise BaseException("执行网易云任务时，服务器返回了错误的数据。")
                for j in result['playlist']['trackIds']:
                    data2 = {}
                    data2["action"] = "play"
                    data2["json"] = {}
                    data2["json"]["download"] = 0
                    data2["json"]["end"] = "playend"
                    data2["json"]["id"] = j["id"]
                    data2["json"]["sourceId"] = ""
                    data2["json"]["time"] = "240"
                    data2["json"]["type"] = "song"
                    data2["json"]["wifi"] = 0
                    buffer.append(data2)
                    count += 1
                    if count >= 310:
                        break
                if count >= 310:
                    break

            url = "http://music.163.com/weapi/feedback/weblog"
            postdata = {
                "logs": json.dumps(buffer)
            }
            result = wyy.post(url, protect(json.dumps(postdata))).json()
            if 'code' not in result:
                raise BaseException("执行网易云任务时，服务器返回了错误的数据。")
            if result['code'] == 200:
                message += f"刷单{count}首"
                messageX += f"已刷单，刷单{count}首。返回数据：<br>" + str(result)
                add_userlog(info['username'], partname + '> 返回结果：' + f"已刷单，刷单{count}首。返回数据：" + str(result))
            else:
                raise BaseException("执行网易云音乐刷单时，出现了错误！错误如下：" + result['message'])

            add_retrndata(info['partcard'], [message, messageX])
        except BaseException as error:
            add_userlog(info['username'], partname + '> 任务失败！原因如下：' + str(error))
            add_retrndata(info['partcard'], ['任务失败', '任务失败！原因如下：' + str(error)])
        printW(f"任务编号:{times},任务ID:{info['partcard']},执行完毕！", who=partname)
    printW("签到任务完成！", who=partname)


def QQmusic_Dosign():
    """QQ音乐 自动领取积分  gtk获取方法来自：https://www.cnblogs.com/jqdemo/p/3139794.html"""
    partname = "QQmusic"
    printW("签到任务执行中......", who=partname)
    printW("获取任务数量中......", who=partname)
    userdata = get_userdata(partname)
    printW("签到任务总数：", len(userdata), "个", who=partname)
    times = 1  # 记录次数
    for info in userdata:
        if 'partcard' not in info:
            continue
        printW(f"任务编号:{times},任务ID:{info['partcard']},正在执行......", who=partname)
        try:
            # 获取条件
            def getGTK():
                string = "@HR3etVm80"
                hash = 5381
                for i in range(len(string)):
                    hash += (hash << 5) + ord(string[i])
                return hash & 0x7fffffff

            cookie = info['cookie']
            # sign = info['sign']
            gtk = str(getGTK())
            uin = re.compile(';uin=\d*;| uin=\d*;').search(cookie).group().strip().replace("uin=", "").replace(";", "")

            # 构造请求数据
            data = '{"req_0":{"module":"UserGrow.UserGrowScore","method":"receive_score","param":{"musicid":"' + uin + '","type":15}},"comm":{"g_tk":' + gtk + ',"uin":' + uin + ',"format":"json","ct":23,"cv":0}}'
            headers = {"User-Agent": user_agent, "Cookie": cookie}

            result = requests.post("https://u.y.qq.com/cgi-bin/musicu.fcg", headers=headers, data=data).json()

            if ('req_0' in result and 'data' in result['req_0']) == False:
                raise BaseException("用户登录信息失效！cookie已经失效！请重新配置！")

            ret = result['req_0']['data']
            if 'retCode' in ret and ret['retCode'] == 0:
                add_userlog(info['username'],
                            partname + '> 签到返回结果：' + f"QQ音乐签到成功：连续签到天数{ret['totalDays']}，总积分{ret['totalScore']}个")
                add_retrndata(info['partcard'], [f"连续签到天数 {ret['totalDays']} 天，总积分 {ret['totalScore']} 个",
                                                 f"QQ音乐签到成功：连续签到天数{ret['totalDays']}，总积分{ret['totalScore']}个，返回数据详情：" + str(
                                                     ret)])
            elif ret['retCode'] == 40001:
                add_userlog(info['username'], partname + '> 签到返回结果：' + f"QQ音乐重复签到。返回数据：" + str(ret))
                add_retrndata(info['partcard'], [f"重复签到",
                                                 f"QQ音乐重复签到返回数据详情：" + str(ret)])
            else:
                raise BaseException("QQ音乐返回了未定义的代码数据如下：" + str(ret))

        except BaseException as error:
            add_userlog(info['username'], partname + '> 签到任务失败！原因如下：' + str(error))
            add_retrndata(info['partcard'], ['签到失败', '签到任务失败！原因如下：' + str(error)])
        printW(f"任务编号:{times},任务ID:{info['partcard']},执行完毕！", who=partname)
    printW("签到任务完成！", who=partname)


def ydybj_Dosign():
    """有道云笔记"""
    partname = "ydybj"
    printW("签到任务执行中......", who=partname)
    printW("获取任务数量中......", who=partname)
    userdata = get_userdata(partname)
    printW("签到任务总数：", len(userdata), "个", who=partname)
    times = 1  # 记录次数
    for info in userdata:
        if 'partcard' not in info:
            continue
        printW(f"任务编号:{times},任务ID:{info['partcard']},正在执行......", who=partname)
        try:
            time.sleep(1)  # 防止“和谐”机制

            login_data = requests.post('https://note.youdao.com/yws/api/daupromotion?method=sync',
                                       headers={'user-agent': 'ynote-android',
                                                'Cookie': info['cookie']}).content.decode('utf-8')
            time.sleep(1)  # 防止“和谐”机制
            dosign_data = requests.post('https://note.youdao.com/yws/mapi/user?method=checkin',
                                        headers={'user-agent': 'ynote-android',
                                                 'Cookie': info['cookie']}).content.decode('utf-8')
            time.sleep(1)  # 防止“和谐”机制
            user_data = requests.post('https://note.youdao.com/yws/mapi/user?method=get',
                                      headers={'user-agent': 'ynote-android', 'Cookie': info['cookie']}).json()

            if "error" in user_data:
                raise BaseException(user_data['message'])
            q_size = round(int(user_data['q']) / 1024 / 1024 / 1024, 2)
            u_size = round(int(user_data['u']) / 1024 / 1024 / 1024, 2)

            add_userlog(info['username'],
                        partname + '> 签到返回结果：' + f'有道云目前使用量：{u_size}G / {q_size}G ，登录奖励数据：{login_data}  签到返回数据：{dosign_data}')
            add_retrndata(info['partcard'], [f"已签到，空间容量： {q_size} / {u_size} G",
                                             f'有道云目前使用量：{u_size}G / {q_size}G ，登录奖励数据：{login_data}  签到返回数据：{dosign_data}'])
        except BaseException as error:
            add_userlog(info['username'], partname + '> 签到任务失败！原因如下：' + str(error))
            add_retrndata(info['partcard'], ['签到失败', '签到任务失败！原因如下：' + str(error)])
        printW(f"任务编号:{times},任务ID:{info['partcard']},执行完毕！", who=partname)
    printW("签到任务完成！", who=partname)


def jrtt_Dosign():
    """今日头条"""
    partname = "jrtt"
    printW("签到任务执行中......", who=partname)
    printW("获取任务数量中......", who=partname)
    userdata = get_userdata(partname)
    printW("签到任务总数：", len(userdata), "个", who=partname)
    times = 1  # 记录次数
    for info in userdata:
        if 'partcard' not in info:
            continue
        printW(f"任务编号:{times},任务ID:{info['partcard']},正在执行......", who=partname)
        try:
            cookie = info['cookie']
            print(cookie)

            headers = {
                'Host': 'api3-normal-c-hl.snssdk.com',
                'Connection': 'keep-alive',
                'Content-Length': '0',
                'X-SS-REQ-TICKET': '1614153220185',
                'x-tt-dt': 'AAAVGVQFKFA7YJFFSVVSIEAHHWFBWH4T5SN62C42HPWPV4ZR453AH7WJODTGZQ4PJ2Z6HZFYZ7WPFQN6TA7WT2GUSCWTNZWH4KO3GZXGHLKQ5Z7IUH47LNESQDYHM',
                'sdk-version': '2',
                'X-Tt-Token': '0042d3fc0ac708471463e9517f6532b2dc0256d2ac31f7df24941d9b4cd2d5319ed1a0ccaf8bb757cf70100fe4d644e4e8e70713a5b7e072704033912b56718109f45f469f0acf8d8476fc07385a83e4fc94b81b9bb00c13800dbfac1b899e76c00db-1.0.1',
                'passport-sdk-version': '30',
                'x-vc-bdturing-sdk-version': '2.0.0',
                'User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 10; JER-TN20 Build/HUAWEIJER-TN20) NewsArticle/8.1.4 cronet/TTNetVersion:1c8b77ac 2020-12-16 QuicVersion:47946d2a 2020-10-14',
                'Content-Type': 'application/json; charset=utf-8',
                'X-SS-STUB': 'BFA1A97C900EC7971C7E835B4FBC206A',
                'X-SS-DP': '35',
                'x-tt-trace-id': '00-d30781d30d27fab77802a6db81af0023-d30781d30d27fab7-01',
                'Accept-Encoding': 'gzip, deflate, br',
                'X-Khronos': '1614153220',
                'X-Gorgon': '040400ca0000086700789ce28f335c940dedc598a7d47fa993e3',
                'X-Tyhon': '7n8RByY4PyUQHWAmJwgyCRMpZyRCfAV4Exsn+lE=',
                'Cookie': cookie
            }

            result = requests.post(
                "https://api3-normal-c-hl.snssdk.com/luckycat/lite/v1/sign_in/action?manifest_version_code=8140&_rticket=1614153220182&_rticket=1614153220184&sa_enable=0&iid=493008015856627&channel=lite_huawei&use_ecpm=0&device_type=JER-TN20&language=zh&ab_client=a1%2Ce1%2Cf2%2Cg2%2Cf7&resolution=1080*2214&openudid=08a7745fb9c50514&is_pad=0&update_version_code=81409&status_bar_height=42&cdid=d5ea33ce-67ed-4797-8924-b1e688db84c0&ab_group=z2&os_api=29&mac_address=B6%3A1C%3A11%3A7D%3A9F%3AF9&abflag=3&rit=coin&dpi=480&oaid=6d0e2ae1-e6b1-4aee-a14e-eadbce755931&ab_feature=z1&cookie_data=r_u2-HiXkcdan2zGuClpiA&ac=wifi&act_hash=ed6ed68b5b6baa3bf0dfff8f901106d7&device_id=703324374444653&pass_through=default&os_version=10&version_code=814&tma_jssdk_version=1.95.0.24&app_name=news_article_lite&ab_version=2424977%2C668903%2C668904%2C2424941%2C668907%2C2424983%2C668905%2C2424950%2C1859936%2C2424959%2C668906%2C2424987%2C668908&storage_left=0&version_name=8.1.4&device_brand=HUAWEI&ssmix=a&plugin_state=3342018441245&device_platform=android&polaris_version=1.0.5&aid=35&_request_from=web&rom_version=emotionui_11.0.0_jer-tn20+11.0.0.141%28c01e140r7p1%29&luckycat_version_name=3.2.0-rc.24&luckycat_version_code=320024"
                , headers=headers, data='{"rit":"coin","use_ecpm":0}').json()

            if 'err_tips' not in result or 'err_no' not in result:
                raise BaseException("服务器返回未定义的数据！")

            if result['err_no'] == 0:
                add_userlog(info['username'], partname + '> 签到返回结果：' + str(result))
                add_retrndata(info['partcard'],
                              [f"增加 {result['data']['new_excitation_ad']['score_amount']} 金币", f"签到成功：{str(result)}"])
            elif result['err_no'] == 4:
                add_userlog(info['username'], partname + '> 签到返回结果：' + str(result))
                add_retrndata(info['partcard'],
                              ["重复签到", f"签到成功： {str(result)}"])
            else:
                add_userlog(info['username'], partname + '> 签到返回结果：' + str(result))
                add_retrndata(info['partcard'],
                              ["签到失败", f"签到数据： {str(result)}"])

        except BaseException as error:
            add_userlog(info['username'], partname + '> 签到任务失败！原因如下：' + str(error))
            add_retrndata(info['partcard'], ['签到失败', '签到任务失败！原因如下：' + str(error)])
        printW(f"任务编号:{times},任务ID:{info['partcard']},执行完毕！", who=partname)
    printW("签到任务完成！", who=partname)


# ---------------  到这里结束写自动签到函数 ---------------
try:
    db = pymysql.connect(user=sqlUsername, password=sqlPassword, host=sqlServer, port=sqlPort,
                         database=sqlName)  # 连接数据库
    cursor = db.cursor(pymysql.cursors.DictCursor)  # 使用 cursor() 方法创建一个游标对象 cursor
    reconnect_db()
except BaseException as error:
    printW("连接数据库失败！程序正在退出......失败原因：" + str(error))
    exit()

printW("正在测试数据库读写......")
try:
    test_db()
except BaseException as error:
    printW("数据库读写有误：" + str(error))
    printW("程序正在退出......")
    exit()
printW("正在测试网站连接......")
try:
    requests.get('https://www.baidu.com')
except BaseException as error:
    printW("无法连接网站（请不要开抓包软件或者代理）：" + str(error))
    printW("程序正在退出......")
    exit()
if emailDing and emailTest:
    printW("正在测试发邮件......")
    if send_email(emailTestEmail, 'Webpy测试发送邮件用户', '来自Webpy的一封信', '<p>如果您成功看到了这封邮件，恭喜您邮件服务器配置成功！</p>'):
        printW("测试发送邮件成功！")
    else:
        printW("测试发送邮件失败！请检查配置！若您不想使用发送邮件，请将 emailDing 改为 False ！程序正在退出......")
        exit()

printW("自检已完成！所用时间：", round((time.time() - checktime_start) * 1000, 2), "ms")

# 获取最新时间
latest_time_stamp = time.time()
latest_time = time.localtime(latest_time_stamp)

# 天
day_time_stamp = latest_time_stamp
day_time = latest_time

# 小时
hour_time = latest_time

# 分钟
min_time = latest_time

# 防止天任务过度执行
day_error = 0

printW("进入程序循环......")
while True:
    time.sleep(1)  # 每次循环的延迟 可以适当调低 推荐 1s ~ 10s

    latest_time_stamp = time.time()
    latest_time = time.localtime(latest_time_stamp)

    if latest_time.tm_min != min_time.tm_min:
        # 分钟
        printW("分钟任务正在执行......")

        printW("分钟任务执行完毕！")
        min_time = time.localtime(time.time())

    if latest_time.tm_hour != hour_time.tm_hour:
        # 小时
        printW("小时任务正在执行......")

        try:
            reconnect_db()
            printW("连接数据库完毕！")
            test_db()
        except BaseException as error:
            printW("重新连接数据库失败！下个循环重连......")
            continue

        printW("小时任务执行完毕！")
        hour_time = time.localtime(time.time())

    if latest_time.tm_yday != day_time.tm_yday:
        # 天
        sleep = random.randint(60, 300)

        printW(f"天任务将在{sleep}s后执行！")
        time.sleep(sleep)  # 1 ~ 5 分钟随机延迟
        printW("天任务正在执行......")

        day_error += 1
        if day_error > 4:
            day_time_stamp = time.time()
            day_time = time.localtime(day_time_stamp)
            day_error = 0
            printW("天任务强制跳过执行！")

        try:
            reconnect_db()
            printW("连接数据库完毕！")
            test_db()
        except BaseException as error:
            printW("重新连接数据库失败！下个循环重连......")
            continue

        Acfun_Dosign()  # Acfun自动签到
        BiliBili_Dosign()  # BiliBili自动签到
        wyy_music_Dosign()  # 网易云自动
        iqiyi_Dosign()  # 爱奇艺
        QQmusic_Dosign()  # QQ音乐
        ydybj_Dosign()  # 有道云
        jrtt_Dosign()  # 今日头条

        email_report()  # 报告

        printW("天任务执行完毕！")

        day_time_stamp = time.time()
        day_time = time.localtime(day_time_stamp)
